import * as fs from 'fs';

import * as AntlrUtils from './AntlrUtils';
import { PositionAwareListener } from './PositionAwareListener';
import { ArkcubeScriptParser, FunctionApplyContext, FunctionDeclContext, FunctionDeclStatementContext, ProgramContext } from '../gen/ArkcubeScriptParser';
import { ArkcubeScriptVisitor } from '../gen/ArkcubeScriptVisitor';
import { ArkcubeScriptLexer } from '../gen/ArkcubeScriptLexer';

import { ParseTree } from 'antlr4ts/tree/ParseTree';
import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor'
import { RuleNode } from 'antlr4ts/tree/RuleNode';
import { TerminalNode } from 'antlr4ts/tree/TerminalNode';
import { ErrorNode } from 'antlr4ts/tree/ErrorNode';
import { Token } from 'antlr4ts/Token';
import { TokenStreamRewriter } from 'antlr4ts/TokenStreamRewriter';
import { BufferedTokenStream } from 'antlr4ts/BufferedTokenStream';
import { CharStreams, CommonTokenStream, ParserRuleContext } from 'antlr4ts';

import { TextDocument } from 'vscode-languageserver-textdocument';
import { Location, Position } from 'vscode-languageserver/node';
import { URI } from 'vscode-uri';
import { Diagnostic } from 'vscode-languageserver';


export class DocumentManager {

	private documents: { [uri: string]: TextDocument } = {};
	public documentASTs: { [uri: string]: ProgramContext } = {};
	constructor() {
	}

	updateDocument(textDocument: TextDocument): void {
		this.documents[textDocument.uri] = textDocument;
		this.documentASTs[textDocument.uri] = this.createAST(textDocument);
	}

	getOrCreateDocument(uri: string): TextDocument | null {
		if (this.documents[uri]) {
			return this.documents[uri];
		}

		const filePath = URI.parse(uri).fsPath;
		if (!fs.existsSync(filePath)) {
			return null;
		}
		let content = fs.readFileSync(filePath, 'utf-8');
		return TextDocument.create(uri, 'arkcubescript', 1, content);
	}

	getOrCreateAST(uri: string): ProgramContext | null {
		if (this.documentASTs[uri]) {
			return this.documentASTs[uri];
		}

		const textDocument = this.getOrCreateDocument(uri);
		if (textDocument == null) {
			return null;
		}

		return this.createAST(textDocument);
	}

	createAST(textDocument: TextDocument): ProgramContext {
		// Create the lexer and parser 
		let inputStream = CharStreams.fromString(textDocument.getText());
		let lexer = new ArkcubeScriptLexer(inputStream);
		let tokenStream = new CommonTokenStream(lexer);
		let parser = new ArkcubeScriptParser(tokenStream);
		let tree: ProgramContext = parser.program();
		this.documentASTs[textDocument.uri] = tree;
		return tree;
	}

	getPositionAst(uri: string, position: Position): ParserRuleContext | TerminalNode | null {
		const ast = this.getOrCreateAST(uri);
		if (!ast) return null;
		return PositionAwareListener.getPositionAst(position, ast);
	}

}
